home *** CD-ROM | disk | FTP | other *** search
/ PC-X 1997 October / pcx14_9710.iso / swag / delphi.swg / 0167_Nice Handling of Keyboard keys.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-08-30  |  11.7 KB  |  375 lines

  1.  
  2. Here is the poor man's version:
  3.  
  4.  procedure keybd_Event; far; external 'USER' index 289;
  5.  
  6.  procedure PostVKey(bVirtKey: byte; Up: Boolean);
  7.  var
  8.   AXReg,BXReg : Word;
  9.   AXHigh, AXLow, BXHigh, BXLow : Byte;
  10.  
  11. function MakeWord(L,H: Byte): Word;
  12. begin
  13.   MakeWord := (H shl 8) + L;
  14. end;
  15. begin
  16.   AXLow := bVirtKey;
  17.   if up then AXHigh := $80 else AXHigh := $0;
  18.   AXreg := MakeWord(AXLow,AXHigh);
  19.   BXLow := VkKeyScan(bVirtKey);
  20.   BXHigh := 0;
  21.   BXReg := MakeWord(BXLow,BXHigh);
  22.   asm
  23.     mov bx,BXreg;
  24.     mov ax,AXReg;
  25.   end;
  26.   Keybd_Event;
  27. end;
  28.  
  29. then to simulate Shift+Ins you need:-
  30.  
  31. PostVKey(VK_Shift,false);
  32. PostVKey(VK_Insert,false);
  33. PostVKey(VK_Insert,True);
  34. PostVKey(VK_Shift,True);
  35.  
  36. Here is the Rolls-Royce version:
  37. Note:  This is commercial and copyrighted code.  The source code may not be sold for profit
  38. (unless Steve is doing the selling).
  39.  
  40. {This unit is to be included in the app that you are running.}
  41. unit SKeys;
  42. interface
  43. type
  44.   { Return values for SendKeys function }
  45.   TSendKeyError = (sk_None, sk_FailSetHook, sk_InvalidToken, sk_UnknownError);
  46. function SendKeys(S: String): TSendKeyError;
  47. implementation
  48. function SendKeys; external 'SendKey' index 2;
  49. end.
  50. (********************************************)
  51. {Here is the DLL that is used.}
  52. library SendKey;
  53. uses
  54.  SysUtils, WinTypes, WinProcs, Messages, Classes, KeyDefs;
  55. type
  56.   { Error codes }
  57.   TSendKeyError = (sk_None, sk_FailSetHook, sk_InvalidToken, sk_UnknownError);
  58.   { exceptions }
  59.   ESendKeyError = class(Exception);
  60.   ESetHookError = class(ESendKeyError);
  61.   EInvalidToken = class(ESendKeyError);
  62.   { a TList descendant that know how to dispose of its contents }
  63.   TMessageList = class(TList)
  64.   public
  65.     destructor Destroy; override;
  66.   end;
  67. destructor TMessageList.Destroy;
  68. var
  69.   i: longint;
  70. begin
  71.   { deallocate all the message records before discarding the list }
  72.   for i := 0 to Count - 1 do
  73.     Dispose(PEventMsg(Items[i]));
  74.   inherited Destroy;
  75. end;
  76. var
  77.   { variables global to the DLL }
  78.   MsgCount: word;
  79.   MessageBuffer: TEventMsg;
  80.   HookHandle: hHook;
  81.   Playing: Boolean;
  82.   MessageList: TMessageList;
  83.   AltPressed, ControlPressed, ShiftPressed: Boolean;
  84.   NextSpecialKey: TKeyString;
  85.  
  86. function MakeWord(L, H: Byte): Word;
  87. { macro creates a word from low and high bytes }
  88. inline(
  89.   $5A/            { pop dx }
  90.   $58/            { pop ax }
  91.   $8A/$E2);       { mov ah, dl }
  92.  
  93. procedure StopPlayback;
  94. { Unhook the hook, and clean up }
  95. begin
  96.   { if Hook is currently active, then unplug it }
  97.   if Playing then
  98.     UnhookWindowsHookEx(HookHandle);
  99.   MessageList.Free;
  100.   Playing := False;
  101. end;
  102.  
  103. function Play(Code: integer; wParam: word; lParam: Longint): Longint; export;
  104.  
  105. { This is the JournalPlayback callback function.  It is called by Windows }
  106. { when Windows polls for hardware events.  The code parameter indicates what }
  107. { to do. }
  108. begin
  109.   case Code of
  110.     hc_Skip: begin
  111.     { hc_Skip means to pull the next message out of our list. If we }
  112.     { are at the end of the list, it's okay to unhook the JournalPlayback }
  113.     { hook from here. }
  114.       { increment message counter }
  115.       inc(MsgCount);
  116.       { check to see if all messages have been played }
  117.       if MsgCount >= MessageList.Count then
  118.         StopPlayback
  119.       else
  120.       { copy next message from list into buffer }
  121.       MessageBuffer := TEventMsg(MessageList.Items[MsgCount]^);
  122.       Result := 0;
  123.     end;
  124.     hc_GetNext: begin
  125.     { hc_GetNext means to fill the wParam and lParam with the proper }
  126.     { values so that the message can be played back.  DO NOT unhook }
  127.     { hook from within here.  Return value indicates how much time until }
  128.     { Windows should playback message.  We'll return 0 so that it's }
  129.     { processed right away. }
  130.       { move message in buffer to message queue }
  131.       PEventMsg(lParam)^ := MessageBuffer;
  132.       Result := 0  { process immediately }
  133.     end
  134.     else
  135.       { if Code isn't hc_Skip or hc_GetNext, then call next hook in chain }
  136.       Result := CallNextHookEx(HookHandle, Code, wParam, lParam);
  137.   end;
  138. end;
  139. procedure StartPlayback;
  140. { Initializes globals and sets the hook }
  141. begin
  142.   { grab first message from list and place in buffer in case we }
  143.   { get a hc_GetNext before and hc_Skip }
  144.   MessageBuffer := TEventMsg(MessageList.Items[0]^);
  145.   { initialize message count and play indicator }
  146.   MsgCount := 0;
  147.   { initialize Alt, Control, and Shift key flags }
  148.   AltPressed := False;
  149.   ControlPressed := False;
  150.   ShiftPressed := False;
  151.   { set the hook! }
  152.   HookHandle := SetWindowsHookEx(wh_JournalPlayback, Play, hInstance, 0);
  153.   if HookHandle = 0 then
  154.     raise ESetHookError.Create('Couldn''t set hook')
  155.   else
  156.     Playing := True;
  157. end;
  158. procedure MakeMessage(vKey: byte; M: word);
  159. { procedure builds a TEventMsg record that emulates a keystroke and }
  160. { adds it to message list }
  161. var
  162.   E: PEventMsg;
  163. begin
  164.   New(E);                                 { allocate a message record }
  165.   with E^ do begin
  166.     Message := M;                         { set message field }
  167.     { high byte of ParamL is the vk code, low byte is the scan code }
  168.     ParamL := MakeWord(vKey, MapVirtualKey(vKey, 0));
  169.     ParamH := 1;                          { repeat count is 1 }
  170.     Time := GetTickCount;                 { set time }
  171.   end;
  172.   MessageList.Add(E);
  173. end;
  174. procedure KeyDown(vKey: byte);
  175. { Generates KeyDownMessage }
  176. begin
  177.   { don't generate a "sys" key if the control key is pressed (Windows quirk) }
  178.   if (AltPressed and (not ControlPressed) and (vKey in [Ord('A')..Ord('Z')])) or
  179.      (vKey = vk_Menu) then
  180.     MakeMessage(vKey, wm_SysKeyDown)
  181.   else
  182.     MakeMessage(vKey, wm_KeyDown);
  183. end;
  184. procedure KeyUp(vKey: byte);
  185. { Generates KeyUp message }
  186. begin
  187.   { don't generate a "sys" key if the control key is pressed (Windows quirk) }
  188.   if AltPressed and (not ControlPressed) and (vKey in [Ord('A')..Ord('Z')]) then
  189.     MakeMessage(vKey, wm_SysKeyUp)
  190.   else
  191.     MakeMessage(vKey, wm_KeyUp);
  192. end;
  193. procedure SimKeyPresses(VKeyCode: Word);
  194. { This function simulates keypresses for the given key, taking into }
  195. { account the current state of Alt, Control, and Shift keys }
  196. begin
  197.   { press Alt key if flag has been set }
  198.   if AltPressed then
  199.     KeyDown(vk_Menu);
  200.   { press Control key if flag has been set }
  201.   if ControlPressed then
  202.     KeyDown(vk_Control);
  203.   { if shift is pressed, or shifted key and control is not pressed... }
  204.   if (((Hi(VKeyCode) and 1) <> 0) and (not ControlPressed)) or ShiftPressed then
  205.     KeyDown(vk_Shift);    { ...press shift }
  206.   KeyDown(Lo(VKeyCode));  { press key down }
  207.   KeyUp(Lo(VKeyCode));    { release key }
  208.   { if shift is pressed, or shifted key and control is not pressed... }
  209.   if (((Hi(VKeyCode) and 1) <> 0) and (not ControlPressed)) or ShiftPressed then
  210.     KeyUp(vk_Shift);      { ...release shift }
  211.   { if shift flag is set, reset flag }
  212.   if ShiftPressed then begin
  213.     ShiftPressed := False;
  214.   end;
  215.   { Release Control key if flag has been set, reset flag }
  216.   if ControlPressed then begin
  217.     KeyUp(vk_Control);
  218.     ControlPressed := False;
  219.   end;
  220.   { Release Alt key if flag has been set, reset flag }
  221.   if AltPressed then begin
  222.     KeyUp(vk_Menu);
  223.     AltPressed := False;
  224.   end;
  225. end;
  226. procedure ProcessKey(S: String);
  227. { This function parses each character in the string to create the message list }
  228. var
  229.   KeyCode: word;
  230.   Key: byte;
  231.   index: integer;
  232.   Token: TKeyString;
  233. begin
  234.   index := 1;
  235.   repeat
  236.     case S[index] of
  237.       KeyGroupOpen : begin
  238.       { It's the beginning of a special token! }
  239.         Token := '';
  240.         inc(index);
  241.         while S[index] <> KeyGroupClose do begin
  242.           { add to Token until the end token symbol is encountered }
  243.           Token := Token + S[index];
  244.           inc(index);
  245.           { check to make sure the token's not too long }
  246.           if (Length(Token) = 7) and (S[index] <> KeyGroupClose) then
  247.             raise EInvalidToken.Create('No closing brace');
  248.         end;
  249.         { look for token in array, Key parameter will }
  250.         { contain vk code if successful }
  251.         if not FindKeyInArray(Token, Key) then
  252.           raise EInvalidToken.Create('Invalid token');
  253.         { simulate keypress sequence }
  254.         SimKeyPresses(MakeWord(Key, 0));
  255.       end;
  256.       AltKey : begin
  257.         { set Alt flag }
  258.         AltPressed := True;
  259.       end;
  260.       ControlKey : begin
  261.         { set Control flag }
  262.         ControlPressed := True;
  263.       end;
  264.       ShiftKey : begin
  265.         { set Shift flag }
  266.         ShiftPressed := True;
  267.       end;
  268.       else begin
  269.       { A normal character was pressed }
  270.         { convert character into a word where the high byte contains }
  271.         { the shift state and the low byte contains the vk code }
  272.         KeyCode := vkKeyScan(MakeWord(Byte(S[index]), 0));
  273.         { simulate keypress sequence }
  274.         SimKeyPresses(KeyCode);
  275.       end;
  276.     end;
  277.     inc(index);
  278.   until index > Length(S);
  279. end;
  280. function SendKeys(S: String): TSendKeyError; export;
  281. { This is the one entry point.  Based on the string passed in the S  }
  282. { parameter, this function creates a list of keyup/keydown messages, }
  283. { sets a JournalPlayback hook, and replays the keystroke messages.   }
  284. var
  285.   i: byte;
  286. begin
  287.   try
  288.     Result := sk_None;                   { assume success }
  289.     MessageList := TMessageList.Create;  { create list of messages }
  290.     ProcessKey(S);                       { create messages from string }
  291.     StartPlayback;                       { set hook and play back messages }
  292.   except
  293.     { if an exception occurs, return an error code, and clean up }
  294.     on E:ESendKeyError do begin
  295.       MessageList.Free;
  296.       if E is ESetHookError then
  297.         Result := sk_FailSetHook
  298.       else if E is EInvalidToken then
  299.         Result := sk_InvalidToken;
  300.     end
  301.     else
  302.       { Catch-all exception handler ensures than an exception }
  303.       { doesn't walk up into application stack }
  304.       Result := sk_UnknownError;
  305.   end;
  306. end;
  307. exports
  308.   SendKeys index 2;
  309. begin
  310. end.
  311.  
  312. (********************************************)
  313. unit Keydefs;
  314. interface
  315. uses WinTypes;
  316. const
  317.   MaxKeys = 24;
  318.   ControlKey = '^';
  319.   AltKey = '@';
  320.   ShiftKey = '~';
  321.   KeyGroupOpen = '{';
  322.   KeyGroupClose = '}';
  323. type
  324.   TKeyString = String[7];
  325.   TKeyDef = record
  326.     Key: TKeyString;
  327.     vkCode: Byte;
  328.   end;
  329. const
  330.   KeyDefArray : array[1..MaxKeys] of TKeyDef = (
  331.     (Key: 'F1';     vkCode: vk_F1),
  332.     (Key: 'F2';     vkCode: vk_F2),
  333.     (Key: 'F3';     vkCode: vk_F3),
  334.     (Key: 'F4';     vkCode: vk_F4),
  335.     (Key: 'F5';     vkCode: vk_F5),
  336.     (Key: 'F6';     vkCode: vk_F6),
  337.     (Key: 'F7';     vkCode: vk_F7),
  338.     (Key: 'F8';     vkCode: vk_F8),
  339.     (Key: 'F9';     vkCode: vk_F9),
  340.     (Key: 'F10';    vkCode: vk_F10),
  341.     (Key: 'F11';    vkCode: vk_F11),
  342.     (Key: 'F12';    vkCode: vk_F12),
  343.     (Key: 'INSERT'; vkCode: vk_Insert),
  344.     (Key: 'DELETE'; vkCode: vk_Delete),
  345.     (Key: 'HOME';   vkCode: vk_Home),
  346.     (Key: 'END';    vkCode: vk_End),
  347.     (Key: 'PGUP';   vkCode: vk_Prior),
  348.     (Key: 'PGDN';   vkCode: vk_Next),
  349.  
  350.     (Key: 'TAB';    vkCode: vk_Tab),
  351.     (Key: 'ENTER';  vkCode: vk_Return),
  352.     (Key: 'BKSP';   vkCode: vk_Back),
  353.     (Key: 'PRTSC';  vkCode: vk_SnapShot),
  354.     (Key: 'SHIFT';  vkCode: vk_Shift),
  355.     (Key: 'ESCAPE'; vkCode: vk_Escape));
  356.  
  357. function FindKeyInArray(Key: TKeyString; var Code: Byte): Boolean;
  358. implementation
  359. uses SysUtils;
  360. function FindKeyInArray(Key: TKeyString; var Code: Byte): Boolean;
  361. { function searches array for token passed in Key, and returns the }
  362. { virtual key code in Code. }
  363. var
  364.   i: word;
  365. begin
  366.   Result := False;
  367.   for i := Low(KeyDefArray) to High(KeyDefArray) do
  368.     if UpperCase(Key) = KeyDefArray[i].Key then begin
  369.       Code := KeyDefArray[i].vkCode;
  370.       Result := True;
  371.       Break;
  372.     end;
  373. end;
  374. end.
  375.